﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Markup;

using alib.Character;
using alib.Debugging;
using alib.Enumerable;
using alib.Reflection;
using alib.Hmm;

using agree.schema;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class PosTrigramModel : _monad<String>,
		ρρFilename
	{
		public PosTrigramModel(IRtParent parent, String filename)
			: base(parent, filename)
		{
			_start_loading();
		}
		public PosTrigramModel(IRtParent parent)
			: base(parent)
		{
		}

		Task<StringHmm> loading;

		Task<StringHmm> _start_loading()
		{
			var _tmp = loading;
			if (_tmp == null)
			{
				_tmp = new Task<StringHmm>(() => new StringHmm(t0));
				if (_tmp == (_tmp = Interlocked.CompareExchange(ref loading, _tmp, null) ?? _tmp))
					_tmp.Start();
			}
			return _tmp;
		}


		public StringHmm Model { get { return _start_loading().Result; } }

		public PosProb[][] Decode(String[] sentence)
		{
#if DEBUG
#warning cleanup debugging code
			// You may rest, camp and spend the night in Byfjellene.
			if (sentence.SequenceEqual("You may rest , camp and spend the night in Byfjellene .".Split()))
				return _foo;

			if (sentence.SequenceEqual("Cats sleep .".Split()))
				return _cats_sleep;
#endif

			return this.Model.Decode(sentence);
		}
#if DEBUG
#warning cleanup debugging code
		static PosProb[][] _foo = new PosProb[][] {
			new[] { new PosProb("PRP",1.0) },
			new[] { new PosProb("MD",1.0) },
			new[] { new PosProb("VB",7.821188e-01), new PosProb("NN",2.178812e-01) },
			new[] { new PosProb(",",1.0) },
			new[] { new PosProb("NN",1.0) },
			new[] { new PosProb("CC",1.0) },
			new[] { new PosProb("VB",9.216283e-01),new PosProb("VBP",7.837169e-02) },
			new[] { new PosProb("DT",1.0) },
			new[] { new PosProb("NN",1.0) },
			new[] { new PosProb("IN",1.0) },
			new[] { new PosProb("NNP",9.212658e-01),new PosProb("NN",7.873421e-02) },
			new[] { new PosProb(".",1.0) },
		};

		static PosProb[][] _cats_sleep = new PosProb[][] {
			new[] { new PosProb("NNS",1.0) },
			new[] { new PosProb("VBP",.67), new PosProb("NN",.25) ,new PosProb("VB",.077)},
			new[] { new PosProb(".",1.0)},
		};
#endif

		///////////////////////////////////////////////////////////////////////
		///
		public String Filename
		{
			get { return t0; }
			set
			{
				if (t0 != null)
					throw new ArgumentException("'Filename' is write-once");
				if (value == null)
					return;
				t0 = value;
				_start_loading();
			}
		}
		/// 
		///////////////////////////////////////////////////////////////////////

		public override object ProvideValue(IServiceProvider serviceProvider)
		{
			if (t0 == null)
				throw new ArgumentNullException("'Filename' is required");

			_start_loading();
			return this;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class LowercaseFirstWordFirstChar : m_observable<IActiveObj<IString>, IString>, IObserver<IString>
	{
		public LowercaseFirstWordFirstChar(IRtParent parent, IActiveObj<IString> toks)
			: base(parent, toks)
		{
		}

		bool f_first;

		protected override void start(IActiveObj<IString> toks)
		{
			f_first = true;
			toks.Subscribe(this);
		}

		void IObserver<IString>.OnNext(IString value)
		{
			if (f_first)
			{
				var carr = value.Text.ToCharArray();
				carr[0] = Char.ToLowerInvariant(carr[0]);
				var newval = new String(carr);

				/// if we can change the token's text without disturbing its (possibly derived) instance, do so, since
				/// this preserves its incidental/decorating properties, if any. In fact, if we don't do this, most
				/// pipelines will fail when specialized properties are stripped off the first token.
				var si = value as StringItem;
				if (si != null)
					si.Text = newval;
				else
					value = new StringItem(value.Trace, newval);
				f_first = false;
			}

			_item(value);
		}

		void IObserver<IString>.OnCompleted() { _remove_task(); }

		void IObserver<IString>.OnError(Exception error) { _error(error); }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class PosTagger : m_scalar_base<IIdentList<IString>, IdentArray<SourceWord>>,
		ρρPosTrigramModel
	{
		public PosTagger(IRtParent parent, IIdentList<IString> toks)
			: base(parent, toks)
		{
		}

		///////////////////////////////////////////////////////////////////////
		///
		PosTrigramModel model;
		public PosTrigramModel PosTrigramModel
		{
			get { return model; }
			set { model = value; }
		}
		public PosTrigramModel Value
		{
			get { return model; }
			set { model = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////

		protected override IdentArray<SourceWord> start(IIdentList<IString> tokens)
		{
			int i, c_toks = tokens.Count;

			var sent = new String[c_toks];

			for (i = 0; i < c_toks; i++)
				sent[i] = tokens[i].Text;

			var pos_prob_arr = model.Decode(sent);

			var tagged = tokens as IdentArray<SourceWord> ?? new IdentArray<SourceWord>(tokens, c_toks);

			IString istr;
			SourceWord sw;
			for (i = 0; i < c_toks; i++)
			{
				if (tagged == tokens)
					sw = tagged[i];
				else
					sw = tagged[i] = ((istr = tokens[i]) as SourceWord) ?? new SourceWord(tagged, istr.Text);

				//Debug.WriteLine("[" + tagged[i].Text + "/" + tags[i] + "] ");

				sw.PosProbs = pos_prob_arr[i];
#if true
				var ss = sw.Text.ToLower();
				if (ss == "sanit" || ss == "wilat" || ss == "wirat" || ss == "boonsri" || ss == "suree" || ss == "kim")
					sw.PosProbs[0].pos = "NNP";
#endif
			}

			return tagged;
		}
	};
}